/**
 * Copyright (c) 2009 Collaborative Development Group, C.S. Dept., University of Bari
 *
 * All rights reserved. This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License v1.0  which accompanies this distribution, and is 
 * available at http://www.eclipse.org/legal/epl-v10.html
 */
package it.uniba.di.cdg.penelope.util;

import static it.uniba.di.cdg.penelope.util.ReflectionUtilities.*;
import static org.junit.Assert.*;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import it.uniba.di.cdg.penelope.events.OnEvent;

import org.junit.Test;

/**
 * Tests for {@link ReflectionUtilities}.
 */
public class ReflectionUtilitiesTest {

	@Target( ElementType.FIELD )
	@Retention( RetentionPolicy.RUNTIME )
	public @interface TestAnn {}
	
	public static class TestSubject {
		@SuppressWarnings( "unused" ) // Used for testing reflective access
		@TestAnn
		private Object field;
		
		public void someMethod() { /* Nothing */ }
		public void someMethod( Integer i, String s ) { /* Nothing */ }
		
		@OnEvent
		public void annotatedMethod() { }
		
		@SuppressWarnings( "unused" )
		private Integer privateMethod( Integer parm ) {
			return 1;
		}

		@SuppressWarnings( "unused" )
		private Integer privateMethod() {
			return 2;
		}
	}

	@Test
	public void shouldFindMethod() {
		assertNotNull( getMethod( TestSubject.class, "someMethod"  ) );
	}

	@Test
	public void shouldFindMethodWithParms() {
		assertNotNull( getMethod( TestSubject.class, "someMethod", Integer.class, String.class  ) );
	}
	
	@Test( expected = ReflectionException.class )
	public void shouldThrowAnUncheckedExceptionIfMethodIsNotFound() {
		getMethod( TestSubject.class, "unexistingMethod", Integer.class );
	}
	
	@Test
	public void shouldFindAnnotatedMethods() {
		assertEquals( 1, getAnnotatedMethods(TestSubject.class, OnEvent.class ).length );
	}

	@Test
	public void shouldFindAnnotatedFields() {
		assertEquals( 1, getAnnotatedFields( TestSubject.class, TestAnn.class ).length );
	}

	@Test
	public void shouldSetAndGetFieldValue() {
		Object fieldValue = new Object();
		Object target = new TestSubject();
		
		setProperty( target, "field", fieldValue );
		
		assertSame( fieldValue, getProperty( target, "field" ) );
	}

	@Target( ElementType.TYPE )
	@Retention( RetentionPolicy.RUNTIME )
	@Inherited
	public @interface ClassAnn {}

	@Target( ElementType.TYPE )
	@Retention( RetentionPolicy.RUNTIME )
	@Inherited
	public @interface InterfaceAnn {}

	@InterfaceAnn
	public interface Interface {
	}
	
	@ClassAnn
	public static class Base implements Interface {
		
	}
 	
	public static class Derived extends Base {
	}
	
	@Test
	public void shouldFindTypeAnnotationOnClasses() {
		assertNotNull( scanAnnotation( Derived.class, ClassAnn.class ) );
	}

	@Test
	public void shouldFindTypeAnnotationOnInterfaces() {
		assertNotNull( scanAnnotation( Derived.class, InterfaceAnn.class ) );
	}

	@Test
	public void shouldInvokePrivateMethodWithParms() {
		assertEquals( new Integer( 1 ), (Integer) invokePrivate( new TestSubject(), "privateMethod", 1 ) );
	}

	@Test
	public void shouldInvokePrivateMethodWithoutParms() {
		assertEquals( new Integer( 2 ), (Integer) invokePrivate( new TestSubject(), "privateMethod" ) );
	}
}
